import './SetOperationFlow.scss'

import { v4 } from 'uuid'
import { useRef, useState } from 'react'
import { useRequest } from 'alova/client'
import { useEventEmitter, useMount } from 'ahooks'
import { Spin, Result, Button, Dropdown, App } from 'antd'

import { Graph, Shape } from '@antv/x6'
import { register } from '@antv/x6-react-shape'
import { Stencil } from '@antv/x6-plugin-stencil'
import { Transform } from '@antv/x6-plugin-transform'
import { Selection } from '@antv/x6-plugin-selection'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Clipboard } from '@antv/x6-plugin-clipboard'
import { History } from '@antv/x6-plugin-history'

import { Http } from '../../Utils/Api'
import { modelStore } from '../../Config/zustand'
import { Encrypt, Decrypt } from '../../Utils/IndexedDB'

const circleStyle = { r: 4, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', style: { visibility: 'hidden' } }

const nodeStyle = {
    shape: 'custom-react-node',
    width: 100,
    height: 40,
    tools: ['button-remove'],
    attrs: { text: { text: '新增节点', waring: false, params: '' } },
    ports: {
        groups: {
            top: { position: 'top', attrs: { circle: circleStyle } },
            right: { position: 'right', attrs: { circle: circleStyle } },
            bottom: { position: 'bottom', attrs: { circle: circleStyle } },
            left: { position: 'left', attrs: { circle: circleStyle } },
        },
        items: [{ group: 'top' }, { group: 'right' }, { group: 'bottom' }, { group: 'left' }]
    },
}

const connecting = {
    router: 'manhattan',
    connector: { name: 'rounded', args: { radius: 8 } },
    anchor: 'center',
    connectionPoint: 'anchor',
    allowBlank: false,
    snap: { radius: 20 },
}

const attr = { attrs: { line: { stroke: '#A2B1C3', strokeWidth: 2, targetMarker: { name: 'block', width: 12, height: 8 } } }, zIndex: 0 }

const SetOperationFlow = () => {
    const X6Box = useRef()
    const { updateModel } = modelStore()
    const { notification } = App.useApp()
    const [X6, setGX6] = useState()
    const [Active, setActive] = useState()
    const [ListData, setListData] = useState()
    const [ItemData, setItemData] = useState()
    const [NodeData, setNodeData] = useState({})

    const { send } = useRequest(data => Http(data), { immediate: false })

    const onGetData = () => send({ method: 'get', url: 'getProjectFilePath', params: { FilePath: `Process` } }).then(res => setListData(res.data))

    const onGetItem = (fileName) => {
        setItemData()
        setActive(fileName)
        send({ method: 'get', url: 'getProject', params: { uuid: `Process/${fileName.split('.')[0]}` } }).then(async res => {
            const data = await Decrypt(res)
            setItemData(data)
            onInitX6(data)
        })
    }

    const onInitX6 = (res) => {
        if (X6Box.current) {
            const graph = new Graph({
                container: X6Box.current,
                grid: true,
                background: { color: '#ffffff' },
                connecting: { ...connecting, createEdge() { return new Shape.Edge(attr) }, validateConnection({ targetMagnet }) { return !!targetMagnet } },
            })
            graph
                .use(new Transform({ resizing: true, rotating: true }))
                .use(new Selection({ rubberband: true, showNodeSelectionBox: true }))
                .use(new Stencil())
                .use(new Snapline())
                .use(new Keyboard())
                .use(new Clipboard())
                .use(new History())
            graph.fromJSON(res)
            graph.centerContent()
            const showPorts = (ports, show) => { for (let i = 0, len = ports.length; i < len; i += 1) { ports[i].style.visibility = show ? 'visible' : 'hidden' } }
            graph.on('node:mouseenter', () => showPorts(X6Box.current.querySelectorAll('.x6-port-body'), true))
            graph.on('node:mouseleave', () => showPorts(X6Box.current.querySelectorAll('.x6-port-body'), false))
            setGX6(graph)
        } else setTimeout(() => onInitX6(res), 500)
    }

    const onPostData = async (title) => {
        const init = await Encrypt({})
        send({ method: 'post', url: 'posProject', params: { uuid: `Process/${v4()}@${title}`, Content: JSON.stringify(init) } }).then(() => {
            onGetData()
            ProcessEvent.emit({ type: 'closeForm' })
        })
    }

    const onAddProcess = () => {
        updateModel({ title: "新增流程", type: 'AddFormData', width: 480, data: { type: 'AddProcessForm', AddEvnet: ProcessEvent } })
    }

    const onSave = async () => {
        const data = await Encrypt(X6.toJSON())
        const params = { uuid: `Process/${Active.split('.')[0]}`, Content: JSON.stringify(data) }
        send({ method: 'post', url: 'posProject', params }).then(() => notification.success({ message: '保存成功', key: 'flow' }))
    }

    const ProcessEvent = useEventEmitter()
    ProcessEvent.useSubscription(val => {
        const { type, data } = val
        if (type === 'onFormBack') onPostData(data.flowName)
    })

    const NodeEvent = useEventEmitter()
    NodeEvent.useSubscription(val => {
        const { type, data } = val
        if (type === 'onFormBack') {
            NodeData.attr('text', data)
            NodeEvent.emit({ type: 'closeForm' })
        }
    })

    const setButtonClass = (id, text) => {
        const data = X6.getNodes().find(v => v.id === id)
        setNodeData(data)
        updateModel({ title: "节点配置", type: 'AddFormData', width: 480, data: { type: 'ProcessItemForm', AddEvnet: NodeEvent, Initial: text } })
    }

    const CustomComponent = ({ node }) => {
        const { text, waring } = node.attrs.text
        return (
            <Dropdown trigger={['contextMenu']} menu={{
                items: [{
                    key: 'A01',
                    label: (
                        <Button
                            type='line'
                            onClick={() => setButtonClass(node.id, node.attrs.text)}
                        >
                            节点配置
                        </Button>
                    )
                }]
            }} >
                <div className={`custom-react-node${waring ? ' waring' : ''}`}>{text}</div>
            </Dropdown>
        )
    }

    register({ shape: 'custom-react-node', component: CustomComponent })

    useMount(onGetData)

    return (
        <div className='SetOperationFlow'>
            <div className='List'>
                {ListData ? ListData.length !== 0 ?
                    <div className='ListData'>
                        <div className='ListItem'>
                            {ListData.map(({ fileName }) => {
                                const [uuid, name] = fileName.split('@')
                                return (
                                    <div key={uuid} className={Active === fileName ? 'active' : ''} onClick={() => onGetItem(fileName)}>
                                        {name.split('.')[0]}
                                    </div>
                                )
                            })}
                        </div>
                        <Button type="primary" size='large' onClick={onAddProcess}>新增流程</Button>
                    </div> :
                    <Result status="404" title="暂无数据" extra={<Button type="primary" onClick={onAddProcess}>新增流程</Button>} /> :
                    <Spin size='large' />
                }
            </div>

            <div className='Process'>
                {ItemData ?
                    <div className='ProcessTemplate'>
                        <div className='ProcessTemplate__X6' ref={X6Box} />
                        <div className='ProcessTemplate__OPTIONS' >
                            <Button type="primary" size='large' onClick={() => X6.addNode(nodeStyle)}>添加节点</Button>
                            <Button type="primary" size='large' onClick={onSave} >保存数据</Button>
                        </div>
                    </div>
                    : <Spin size='large' />}
            </div>
        </div>
    );
};

export default SetOperationFlow;